🎖️GitЯра🎖️
Commit e71722137f25d9ddfd4c0f35b510426cc4f9854c
Parents : 15568ff
Author : James Rich <2199651+jamesarich@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-06-12T06:06:52-05:00
Committer : GitHub <noreply@github.com>
Date : 2026-06-12T06:06:52-05:00
test: fix wall-clock race in NodeTest.isOnline_usesStrictThresholdBoundary (#5779)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Changes
4 files changed, 17 insertions(+), 9 deletions(-)
Diff
diff --git a/.agent_memory/session_context.archive.md b/.agent_memory/session_context.archive.md
index d2026cebc0..bea22b59c1 100644
--- a/.agent_memory/session_context.archive.md
+++ b/.agent_memory/session_context.archive.md
@@ -2,6 +2,12 @@
# Older handover entries rotated out of session_context.md. Not read by default.
# Consult only if you need historical detail on a specific past change.
+## 2026-05-21 — Activated full on-device token streaming and polished Chirpy's personality instructions
+- Upgraded the on-device inference flow inside `GeminiNanoDocAssistant.kt` to use Firebase AI SDK's reactive `generateContentStream(prompt)` instead of the blocking `generateContent` invocation.
+- Aggregated chunks and emitted incremental `AIDocAssistantResult.Partial` states down the Kotlin Flow, enabling true word-by-word/chunk-by-chunk streaming in the UI for a much more responsive user experience.
+- Refined the `SYSTEM_INSTRUCTION` personality rules for Chirpy to position him as our adorable LoRa radio Node mascot instead of an avian theme, emphasizing high-enthusiasm mesh networking, signal connectivity, battery status, and radio/routing concepts while preserving technical precision.
+- Overhauled system error messages inside `DocsNavigation.kt` and the loading bubble state inside `ChirpyAssistantSheet.kt` to align with the mascot theme.
+
## 2026-05-21 — Implemented streaming chat support and Firebase Remote Config integration for Chirpy
- Added `firebase-config` dependency to Version Catalog `libs.versions.toml` and `androidApp/build.gradle.kts`.
- Added the `AIDocAssistantResult.Partial` variant to support intermediate stream updates.
diff --git a/.agent_memory/session_context.md b/.agent_memory/session_context.md
index ba1b262666..d133856798 100644
--- a/.agent_memory/session_context.md
+++ b/.agent_memory/session_context.md
@@ -6,6 +6,11 @@
# the oldest entries to `session_context.archive.md` (not read by default). The
# "Golden Context" block at the bottom is stable across sessions; keep it here.
+## 2026-06-12 — Fixed flaky NodeTest.isOnline_usesStrictThresholdBoundary (wall-clock race)
+- PR #5779 (targeting main): the test read the clock twice — `onlineTimeThreshold()` once for its expected value, then again inside the `isOnline` getter; a one-second wall-clock tick between reads turned the strict-boundary assertion into `N+1 > N+1` = false. Seen failing on loaded CI in #5760's shard-core (jvm + androidHostTest).
+- Fix: internal `Node.isOnline(threshold: Int)` overload; the public `isOnline` property delegates to it. Test pins one threshold for both construction and check, keeping the strict `>` boundary assertion (no slop widening).
+- Verified: `:core:model:allTests` ×3 (`--rerun-tasks`) all green; full baseline `spotlessApply spotlessCheck detekt assembleDebug test allTests kmpSmokeCompile` 1625 tasks 0 failures.
+
## 2026-06-10 — Fixed Update Changelog workflow crash (failing on every main push since 2026-06-05)
- PR #5769: `.github/workflows/update-changelog.yml` died with `TAG_NAMES: bad array subscript` once v2.7.14 went prod and its `-internal.*`/`-open.*` channel tags were cleaned up — zero channel tags means N=0 and `${TAG_NAMES[$((N-1))]:-$PROD_TAG}` indexes [-1] on an empty array, fatal under `bash -e` before the `:-` fallback applies. Replaced with an explicit `if (( N > 0 ))` branch.
- Second latent bug fixed in the same step: the final `{ ... } > /tmp/unreleased-section.md` group ended with `[ -n ... ] && echo` lists; with SECTIONS and CONTRIBUTORS both empty the group (the script's last command) returns 1 and fails the step even though the file is written. Converted to `if` statements. Previously masked because the prod→HEAD range always had a New Contributors section.
@@ -31,12 +36,6 @@
- Injected this live radio diagnostics context dynamically as a system instruction metadata block on every user query. This empowers the on-device model to answer real-time, personalized diagnostic questions (e.g. "what is my battery level?", "how many active nodes are on my mesh right now?") with 100% on-device offline accuracy.
- Tuned context retrieval constraints for the modern `nano-v4-full` (Gemini Nano v4) model: expanded the total context budget `MAX_CONTEXT_CHARS` from 8,000 to **32,000 characters** (up to ~12K tokens out of the model's native 32K window), and scaled `MAX_PAGE_CHARS` to **16,000 characters** and `MAX_SNIPPET_CHARS` to **8,000 characters** to supply vastly richer, more detailed, and complete documentation fragments.
-## 2026-05-21 — Activated full on-device token streaming and polished Chirpy's personality instructions
-- Upgraded the on-device inference flow inside `GeminiNanoDocAssistant.kt` to use Firebase AI SDK's reactive `generateContentStream(prompt)` instead of the blocking `generateContent` invocation.
-- Aggregated chunks and emitted incremental `AIDocAssistantResult.Partial` states down the Kotlin Flow, enabling true word-by-word/chunk-by-chunk streaming in the UI for a much more responsive user experience.
-- Refined the `SYSTEM_INSTRUCTION` personality rules for Chirpy to position him as our adorable LoRa radio Node mascot instead of an avian theme, emphasizing high-enthusiasm mesh networking, signal connectivity, battery status, and radio/routing concepts while preserving technical precision.
-- Overhauled system error messages inside `DocsNavigation.kt` and the loading bubble state inside `ChirpyAssistantSheet.kt` to align with the mascot theme.
-
## Golden Context (stable across sessions)
- Always check `.skills/compose-ui/strings-index.txt` before reading `strings.xml`.
- Run `python3 scripts/sort-strings.py` after adding strings to keep the index organized.
diff --git a/core/model/src/commonMain/kotlin/org/meshtastic/core/model/Node.kt b/core/model/src/commonMain/kotlin/org/meshtastic/core/model/Node.kt
index 159385415d..754a2462d2 100644
--- a/core/model/src/commonMain/kotlin/org/meshtastic/core/model/Node.kt
+++ b/core/model/src/commonMain/kotlin/org/meshtastic/core/model/Node.kt
@@ -69,7 +69,10 @@ data class Node(
val capabilities: Capabilities by lazy { Capabilities(metadata?.firmware_version) }
val isOnline: Boolean
- get() = lastHeard > onlineTimeThreshold()
+ get() = isOnline(onlineTimeThreshold())
+
+ /** Injectable [threshold] keeps tests deterministic — the property getter re-reads the clock on every access. */
+ internal fun isOnline(threshold: Int): Boolean = lastHeard > threshold
val colors: Pair<Int, Int>
get() { // returns foreground and background @ColorInt for each 'num'
diff --git a/core/model/src/commonTest/kotlin/org/meshtastic/core/model/NodeTest.kt b/core/model/src/commonTest/kotlin/org/meshtastic/core/model/NodeTest.kt
index 779c8e9fd8..2da8c6b1d9 100644
--- a/core/model/src/commonTest/kotlin/org/meshtastic/core/model/NodeTest.kt
+++ b/core/model/src/commonTest/kotlin/org/meshtastic/core/model/NodeTest.kt
@@ -33,8 +33,8 @@ class NodeTest {
fun isOnline_usesStrictThresholdBoundary() {
val threshold = onlineTimeThreshold()
- assertFalse(Node(num = 1, lastHeard = threshold).isOnline)
- assertTrue(Node(num = 1, lastHeard = threshold + 1).isOnline)
+ assertFalse(Node(num = 1, lastHeard = threshold).isOnline(threshold))
+ assertTrue(Node(num = 1, lastHeard = threshold + 1).isOnline(threshold))
}
@Test
Served by rngit 1.5.0 - Generated in 0.15s